/*
  四、手写实现 Mypromise 源码
  要求：尽可能还原 Promise 中的每一个 API，并通过注释的方式描述思路和原理。
*/

const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';

class MyPromise {
  status = PENDING;
  result = undefined;
  reason = undefined;
  // 改成数组是因为一个promise实例可以同时被多次then
  successCallBacks = [];
  errorCallBacks = [];

  resolve = (value) => {
    if (value instanceof MyPromise && value === this) {
      throw new Error('不可以resolve的时候传自己哦')
    }
    if (this.status === PENDING) {
      this.status = FULFILLED;
      this.result = value;
      while (this.successCallBacks.length > 0) {
        this.successCallBacks.shift()();
      }
    }
  }

  reject = (reason) => {
    if (this.status === PENDING) {
      this.status = REJECTED;
      this.reason = reason;
      while (this.errorCallBacks.length > 0) {
        this.errorCallBacks.shift()();
      }
    }
  }

  constructor(executor) {
    try {
      executor(this.resolve, this.reject);
    } catch (error) {
      this.reject(error);
    }
  }

  then(successCallBack, errorCallBack) {
    // 参数可选
    successCallBack = successCallBack ? successCallBack : value => value;
    // 参数可选
    errorCallBack = errorCallBack ? errorCallBack : reason => { throw reason };
    // 开始解决then的返回值，如果then的返回值是个数字，那就直接返回，如果是promise，那就需要等promise执行完毕，再返回给下一个then
    let p = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            // 解决返回值
            let x = successCallBack(this.result);
            // 前面多传一个p是为了检测循环引用，比如一个p的then中又将该p返回了出去，要避免这种循环引用的情况，而这个时候p其实还没实例化，所以通过异步的方式巧妙解决下
            this.__resolvePromise(p, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0)
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            // 解决返回值
            let x = errorCallBack(this.reason);
            // 前面多传一个p是为了检测循环引用，比如一个p的then中又将该p返回了出去，要避免这种循环引用的情况，而这个时候p其实还没实例化，所以通过异步的方式巧妙解决下
            this.__resolvePromise(p, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0)
      } else if (this.status === PENDING) {
        setTimeout(() => {
          try {
            // 为了解决异步的哦
            this.successCallBacks.push(() => {
              let x = successCallBack(this.result);
              this.__resolvePromise(p, x, resolve, reject);
            });
            this.errorCallBacks.push(() => {
              let x = errorCallBack(this.reason);
              this.__resolvePromise(p, x, resolve, reject);
            });
          } catch (error) {
            reject(error);
          }
        }, 0)
      }
    })
    return p;
  }

  // 此方法不应该暴露给外部，显示声明为私有属性
  __resolvePromise(promise, value, resolve, reject) {
    if (value instanceof MyPromise) {
      if (promise === value) {
        throw new Error('发生循环引用啦，error...');
        return
      }
      value.then(res => {
        resolve(res);
      }, err => {
        reject(err);
      })
    } else {
      resolve(value);
    }
  }
}

let p = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve(p);
  }, 1500)
})

// p.then((res) => {
//   console.log(res);
//   return new MyPromise((resolve, reject) => {
//     setTimeout(() => {
//       resolve(11);
//     }, 3000)
//   })
// }, err => console.log(err)).then(res => {
//   console.log(res);
// })


// 发生循环引用的情况
// let p2 = p.then((res) => {
//   console.log(res);
//   return p2;
// })

// 一个promise可以被多个then同时调用
// p.then((res) => {
//   console.log('2');
//   console.log(res);
// })

// p.then((res) => {
//   console.log('3');
//   console.log(res);
// })
